package com.lechi.web.job;

import com.lechi.iot.netty.channel.ITcpChannel;
import com.lechi.iot.netty.utils.BytesToHexUtil;
import com.lechi.web.common.constant.Constants;
import com.lechi.web.common.constant.ScheduleConstants;
import com.lechi.web.common.utils.ExceptionUtil;
import com.lechi.web.common.utils.StringUtils;
import com.lechi.web.common.utils.bean.BeanUtils;
import com.lechi.web.common.utils.spring.SpringUtils;
import com.lechi.web.framework.util.GuavaCacheUtil;
import com.lechi.web.service.IIotJobLogService;
import com.lechi.web.system.domain.IotJobLogBo;
import com.lechi.web.system.domain.IotScheduleJobBo;
import com.lechi.web.util.LeChiCacheUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;

import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Date;
import java.util.stream.Collectors;

/**
 * 抽象quartz调用
 *
 * @author yg
 */
@Slf4j
public abstract class AbstractQuartzJob implements Job {

    /**
     * 线程本地变量
     */
    private static final ThreadLocal<Date> threadLocal = new ThreadLocal<>();

    @Override
    public void execute(JobExecutionContext context) {
        IotScheduleJobBo IotScheduleJobBo = new IotScheduleJobBo();
        BeanUtils.copyBeanProp(IotScheduleJobBo, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES));
        try {
            before(context, IotScheduleJobBo);
            doExecute(context, IotScheduleJobBo);
            after(context, IotScheduleJobBo, null);
        } catch (Exception e) {
            log.error("任务调度执行异常  - ：", e);
            after(context, IotScheduleJobBo, e);
        }
    }

    /**
     * 执行前
     *
     * @param context 工作执行上下文对象
     * @param IotScheduleJobBo  系统计划任务
     */
    protected void before(JobExecutionContext context, IotScheduleJobBo IotScheduleJobBo) {
        threadLocal.set(new Date());
    }

    /**
     * 执行后
     *
     * @param context 工作执行上下文对象
     * @param IotScheduleJobBo  系统计划任务
     */
    protected void after(JobExecutionContext context, IotScheduleJobBo IotScheduleJobBo, Exception e) {
        Date startTime = threadLocal.get();
        threadLocal.remove();
        final IotJobLogBo iotJobLogBo = new IotJobLogBo();
        iotJobLogBo.setJobName(IotScheduleJobBo.getJobName() == null ? "未命名任务" : IotScheduleJobBo.getJobName());
        iotJobLogBo.setDeviceId(IotScheduleJobBo.getDeviceId());
        iotJobLogBo.setInvokeTarget(IotScheduleJobBo.getCronExpression()+ ", cmd=" + IotScheduleJobBo.getCmd());
        iotJobLogBo.setStartTime(startTime);
        iotJobLogBo.setStopTime(new Date());
        iotJobLogBo.setExceptionInfo(IotScheduleJobBo.getRemark());
        long runMs = iotJobLogBo.getStopTime().getTime() - iotJobLogBo.getStartTime().getTime();
        iotJobLogBo.setJobMessage(" 总共耗时：" + runMs + "毫秒");
        if (e != null) {
            iotJobLogBo.setStatus(Constants.FAIL);
            String errorMsg = StringUtils.substring(ExceptionUtil.getExceptionMessage(e), 0, 5000);
            iotJobLogBo.setExceptionInfo(errorMsg);
        } else {
            iotJobLogBo.setStatus(Constants.SUCCESS);
        }
        SpringUtils.getBean(IIotJobLogService.class).addJobLog(iotJobLogBo);
    }

    protected void sendCmd(String hostName, byte[] cmd) {
        ITcpChannel tcpChannel = LeChiCacheUtil.getTcpChannelMap().get(hostName);
        try {
//            TcpMessage message = new TcpByteArrayMessage(cmd);
            ByteBuf byteBuf = Unpooled.wrappedBuffer(cmd);
            tcpChannel.send(byteBuf);
        } catch (Exception e) {
            log.error("定时调度任务发送指令异常{} ，请联系工程师解决！", cmd);
        }
    }

    protected void sendCmd(IotScheduleJobBo job) {
        String hostName = GuavaCacheUtil.getDeviceIdAndIPMap().get(job.getDeviceId());
        if (hostName == null) {
            log.error("定时调度任务发送指令失败，通过设备ID：{} 获取设备地址为空，设备未注册！", job.getDeviceId());
            return;
        }
        if ("hex".equals(job.getDataFormat())) {
            byte[] sendMsg = BytesToHexUtil.hexStrToBinaryStr(job.getCmd());
            sendCmd(hostName, sendMsg);
            return;
        }
        boolean ifAscii = job.getCmd().endsWith("\r\n");
        String cmd = ifAscii ? job.getCmd().substring(0, job.getCmd().length() - 2) : job.getCmd();
        String sendCmd = Arrays.stream(cmd.split(",")).map(param -> getValue(param, job.getDeviceId(), job.getJobName()))
                .collect(Collectors.joining(","));
        job.setJobName(sendCmd);
        sendCmd(hostName,  ifAscii ? sendCmd + "\r\n" : sendCmd);
    }

    protected void sendCmd(String hostName, String cmd) {
        ITcpChannel tcpChannel = LeChiCacheUtil.getTcpChannelMap().get(hostName);
        if (tcpChannel == null) {
            log.error("定时调度任务发送指令失败，通过设备地址：{} 获取设备地址为空，设备未注册！", hostName);
            return;
        }
        log.error("定时调度任务发送指令：{}", cmd);
        try {
            ByteBuf message = ByteBufAllocator.DEFAULT.buffer();
            message.writeCharSequence(cmd, Charset.defaultCharset());
            tcpChannel.send(message);
        } catch (Exception e) {
            log.error("定时调度任务发送指令异常{} ，请联系工程师解决！", cmd);
        }
    }

    protected String getValue(String cmd, Long deviceId, String time) {
        return switch (cmd) {
            case "[&ID]" -> GuavaCacheUtil.getDeviceIdAndInstrumentIdMap().get(deviceId);
            case "[&TIME<yyyymmddhhmmss>]" -> LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
            case "[&TIMESTAMP<yyyymmddhhmm00>]" -> time;
            default -> cmd;
        };
    }

    /**
     * 执行方法，由子类重载
     *
     * @param context 工作执行上下文对象
     * @param IotScheduleJobBo  系统计划任务
     * @throws Exception 执行过程中的异常
     */
    protected abstract void doExecute(JobExecutionContext context, IotScheduleJobBo IotScheduleJobBo) throws Exception;
}
